/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor.ext; import java.util.List; import java.util.Map; import java.util.HashMap; import javax.swing.text.BadLocationException; import javax.swing.event.DocumentListener; import javax.swing.event.DocumentEvent; import org.netbeans.editor.SyntaxSupport; import org.netbeans.editor.AcceptorFactory; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.Utilities; import org.netbeans.editor.TextBatchProcessor; import org.netbeans.editor.FinderFactory; import org.netbeans.editor.Syntax; import org.netbeans.editor.Analyzer; /** * Support methods for syntax analyzes * * @author Miloslav Metelka * @version 1.00 */ public class JavaSyntaxSupport extends ExtSyntaxSupport { // Internal java declaration token processor states static final int INIT = 0; static final int AFTER_TYPE = 1; static final int AFTER_VARIABLE = 2; static final int AFTER_COMMA = 3; static final int AFTER_DOT = 4; static final int AFTER_TYPE_LSB = 5; static final int AFTER_MATCHING_VARIABLE_LSB = 6; static final int AFTER_MATCHING_VARIABLE = 7; private static final int[] COMMENT_TOKENS = new int[] { JavaSyntax.LINE_COMMENT, JavaSyntax.BLOCK_COMMENT }; private static final int[] BRACKET_SKIP_TOKENS = new int[] { JavaSyntax.LINE_COMMENT, JavaSyntax.BLOCK_COMMENT, JavaSyntax.CHAR, JavaSyntax.STRING }; private static final char[] COMMAND_SEPARATOR_CHARS = new char[] { ';', '{', '}' }; private JavaImport javaImport; private boolean javaImportValid; public JavaSyntaxSupport(BaseDocument doc) { super(doc); endVarAcceptor = AcceptorFactory.JAVA_IDENTIFIER; varAcceptor = AcceptorFactory.JAVA_IDENTIFIER_DOT; javaImport = new JavaImport(); } protected void documentModified(DocumentEvent evt) { super.documentModified(evt); javaImportValid = false; } public int[] getCommentTokens() { return COMMENT_TOKENS; } public int[] getBracketSkipTokens() { return BRACKET_SKIP_TOKENS; } public int getLastCommandSeparator(int pos) throws BadLocationException { TextBatchProcessor tbp = new TextBatchProcessor() { public int processTextBatch(BaseDocument doc, int startPos, int endPos, boolean lastBatch) { try { int[] blks = getCommentBlocks(endPos, startPos); FinderFactory.CharArrayBwdFinder cmdFinder = new FinderFactory.CharArrayBwdFinder(COMMAND_SEPARATOR_CHARS); return findOutsideBlocks(cmdFinder, startPos, endPos, blks); } catch (BadLocationException e) { e.printStackTrace(); return -1; } } }; return doc.processText(tbp, pos, 0); } /** Get the class from name. The import sections are consulted to find * the proper package for the name. If the search in import sections fails * the method can ask the finder to search just by the given name. * @param className name to resolve. It can be either the full name * or just the name without the package. * @param searchByName if true and the resolving through the import sections fails * the finder is asked to find the class just by the given name */ public JCClass getClassFromName(String className, boolean searchByName) { refreshJavaImport(); JCClass ret = JCompletion.getPrimitiveClass(className); if (ret == null) { ret = javaImport.getClazz(className); } if (ret == null && searchByName) { List clsList = JCompletion.getFinder().findClasses(null, className, true); if (clsList != null && clsList.size() > 0) { if (clsList.size() > 0) { // more matching classes ret = (JCClass)clsList.get(0); // get the first one } } } return ret; } protected void refreshJavaImport() { if (!javaImportValid) { javaImport.update(doc); } } protected void refreshCompletion() { } /** Get the class that belongs to the given position */ public JCClass getClass(int pos) { return null; } public boolean isStaticBlock(int pos) { return false; } protected DeclarationTokenProcessor createDeclarationTokenProcessor( String varName, int startPos, int endPos) { return new JavaDeclarationTokenProcessor(varName); } protected VariableMapTokenProcessor createVariableMapTokenProcessor( int startPos, int endPos) { return new JavaDeclarationTokenProcessor(null); } class JavaDeclarationTokenProcessor implements DeclarationTokenProcessor, VariableMapTokenProcessor { /** Position of the begining of the declaration to be returned */ int decStartPos = -1; int decArrayDepth; /** Starting position of the declaration type */ int typeStartPos; /** Position of the end of the type */ int typeEndPos; /** Offset of the name of the variable */ int decVarNameOffset; /** Length of the name of the variable */ int decVarNameLen; /** Currently inside parenthesis, i.e. comma delimits declarations */ boolean inParenthesis; /** Depth of the array when there is an array declaration */ int arrayDepth; char[] buffer; int bufferStartPos; String varName; int state; /** Map filled with the [varName, type] pairs * @associates JCType*/ HashMap varMap; /** Construct new token processor * @param varName it contains valid varName name or null to search * for all variables and construct the variable map. */ JavaDeclarationTokenProcessor(String varName) { this.varName = varName; if (varName == null) { varMap = new HashMap(); } } public int getDeclarationPosition() { return decStartPos; } public Map getVariableMap() { return varMap; } private void processDeclaration() { if (varName == null) { // collect all variables String decType = new String(buffer, typeStartPos - bufferStartPos, typeEndPos - typeStartPos); if (decType.indexOf(' ') >= 0) { decType = Analyzer.removeSpaces(decType); } String decVarName = new String(buffer, decVarNameOffset, decVarNameLen); JCClass cls = getClassFromName(decType, true); if (cls != null) { varMap.put(decVarName, JCompletion.getType(cls, decArrayDepth)); } } else { decStartPos = typeStartPos; } } public boolean token(int tokenID, int helperID, int offset, int tokenLen) { int pos = bufferStartPos + offset; switch (tokenID) { case JavaSyntax.KEYWORD: switch (helperID) { case JavaKeywords.BOOLEAN: case JavaKeywords.BYTE: case JavaKeywords.CHAR: case JavaKeywords.DOUBLE: case JavaKeywords.FLOAT: case JavaKeywords.INT: case JavaKeywords.LONG: case JavaKeywords.SHORT: case JavaKeywords.VOID: typeStartPos = pos; arrayDepth = 0; typeEndPos = pos + tokenLen; state = AFTER_TYPE; break; default: state = INIT; break; } break; case JavaSyntax.OPERATOR: switch (helperID) { case JavaSyntax.DOT: switch (state) { case AFTER_TYPE: // allowed only inside type state = AFTER_DOT; typeEndPos = pos + tokenLen; break; default: state = INIT; break; } break; case JavaSyntax.LEFT_SQUARE_BRACKET: switch (state) { case AFTER_TYPE: state = AFTER_TYPE_LSB; arrayDepth++; break; case AFTER_MATCHING_VARIABLE: state = AFTER_MATCHING_VARIABLE_LSB; decArrayDepth++; break; default: state = INIT; break; } break; case JavaSyntax.RIGHT_SQUARE_BRACKET: switch (state) { case AFTER_TYPE_LSB: state = AFTER_TYPE; break; case AFTER_MATCHING_VARIABLE_LSB: state = AFTER_MATCHING_VARIABLE; break; default: state = INIT; break; } break; // both in type and varName case JavaSyntax.LEFT_PARENTHESES: inParenthesis = true; state = INIT; break; case JavaSyntax.RIGHT_PARENTHESES: if (state == AFTER_MATCHING_VARIABLE) { processDeclaration(); } inParenthesis = false; state = INIT; break; case JavaSyntax.LEFT_BRACE: case JavaSyntax.RIGHT_BRACE: inParenthesis = false; // to tolerate opened parenthesis state = INIT; break; case JavaSyntax.COMMA: if (inParenthesis) { // comma is declaration separator in parenthesis if (state == AFTER_MATCHING_VARIABLE) { processDeclaration(); } state = INIT; } else { // not in parenthesis switch (state) { case AFTER_MATCHING_VARIABLE: processDeclaration(); // let it flow to AFTER_VARIABLE case AFTER_VARIABLE: state = AFTER_COMMA; break; default: state = INIT; break; } } break; case JavaSyntax.EQ: // let it flow to SEMICOLON case JavaSyntax.SEMICOLON: if (state == AFTER_MATCHING_VARIABLE) { processDeclaration(); } state = INIT; break; default: state = INIT; break; } break; case JavaSyntax.IDENTIFIER: switch (state) { case AFTER_TYPE: case AFTER_COMMA: if (varName == null || Analyzer.equals(varName, buffer, offset, tokenLen)) { decArrayDepth = arrayDepth; decVarNameOffset = offset; decVarNameLen = tokenLen; state = AFTER_MATCHING_VARIABLE; } else { state = AFTER_VARIABLE; } break; case AFTER_VARIABLE: // error state = INIT; break; case AFTER_DOT: typeEndPos = pos + tokenLen; state = AFTER_TYPE; break; case INIT: typeStartPos = pos; arrayDepth = 0; typeEndPos = pos + tokenLen; state = AFTER_TYPE; break; default: state = INIT; break; } break; case JavaSyntax.TEXT: // whitespace ignored break; default: state = INIT; break; } return true; } public int eot(int offset) { return 0; } public void nextBuffer(char[] buffer, int offset, int len, int startPos, int preScan, boolean lastBuffer) { this.buffer = buffer; bufferStartPos = startPos - offset; } } } /* * Log * 15 Gandalf 1.14 12/28/99 Miloslav Metelka * 14 Gandalf 1.13 11/14/99 Miloslav Metelka * 13 Gandalf 1.12 11/10/99 Miloslav Metelka * 12 Gandalf 1.11 11/9/99 Miloslav Metelka * 11 Gandalf 1.10 11/8/99 Miloslav Metelka * 10 Gandalf 1.9 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 9 Gandalf 1.8 10/10/99 Miloslav Metelka * 8 Gandalf 1.7 9/15/99 Miloslav Metelka * 7 Gandalf 1.6 9/10/99 Miloslav Metelka * 6 Gandalf 1.5 8/27/99 Miloslav Metelka * 5 Gandalf 1.4 8/18/99 Miloslav Metelka * 4 Gandalf 1.3 7/30/99 Miloslav Metelka * 3 Gandalf 1.2 7/20/99 Miloslav Metelka * 2 Gandalf 1.1 6/10/99 Miloslav Metelka * 1 Gandalf 1.0 6/8/99 Miloslav Metelka * $ */